/*    file: CMediaPort.cpp
 *    desc:
 * 
 * created: 2015-11-09
 *  author: chuanjiang.zh@qq.com
 * company: 
 */

#include "stdafx.h"
#include "CMediaPort.h"

#include "Sdp.h"
#include "TFileUtil.h"
#include "Path.h"
#include "Utility.h"
#include "BasicMacro.h"
#include "TStringUtil.h"


static const int PORT_STEP = 100;


CMediaPort::CMediaPort():
    m_connected(false),
    m_running(false)
{

}

CMediaPort::~CMediaPort()
{

}

std::string CMediaPort::getLocalSdp()
{
    if (!m_connected)
    {
        m_localSdp = m_originSdp;
    }
    return m_localSdp;
}

int CMediaPort::setPeerSdp(std::string& peerSdp)
{
    m_connected = true;

    m_peerSdp = peerSdp;

    m_localSdp = apply(m_localSdp, m_peerSdp);

    int rc = setup(m_localSdp, m_peerSdp);

    return rc;
}

std::string CMediaPort::getPeerSdp(std::string& offerSdp)
{
    m_peerSdp = offerSdp;
    m_localSdp = apply(m_originSdp, offerSdp);
    int rc = setup(m_localSdp, offerSdp);
    if (rc != 0)
    {
        return std::string();
    }

    m_connected = true;

    return m_localSdp;
}


int CMediaPort::start()
{
    if (m_running)
    {
        return 0;
    }

    std::string cmdline = comn::StringUtil::format(m_playerPath.c_str(), "local.sdp");

    m_player.open(cmdline.c_str());

    if (!m_vMedium.empty() && !m_vRtpPath.empty())
    {
        cmdline = comn::StringUtil::format(
            "%s -T -f %s -s %d -r %d -d %s %s/%d",
            m_rtpPlayPath.c_str(), m_vRtpPath.c_str(),
            m_vMedium.localPort, m_vMedium.localPort + PORT_STEP, "127.0.0.1",
            m_vMedium.ip.c_str(), m_vMedium.port);
        m_vRtpProc.open(cmdline.c_str());
    }
    
    if (!m_aMedium.empty() && !m_aRtpPath.empty())
    {
        cmdline = comn::StringUtil::format(
            "%s -T -f %s -s %d %s/%d",
            m_rtpPlayPath.c_str(), m_aRtpPath.c_str(),
            m_aMedium.localPort, m_aMedium.ip.c_str(), m_aMedium.port);
        m_aRtpProc.open(cmdline.c_str());
    }

    m_running = true;

    return 0;
}

void CMediaPort::stop()
{
    m_player.close();
    m_vRtpProc.close();
    m_aRtpProc.close();

    m_running = false;
}

void CMediaPort::setOriginSdp(const std::string& sdp)
{
    m_originSdp = sdp;
}

std::string CMediaPort::apply(const std::string& localSdp, const std::string& peerSdp)
{
    sdp::SessionDescription local;
    local.parse(localSdp);

    sdp::SessionDescription peer;
    peer.parse(peerSdp);

    sdp::SessionDescription sdp;
    sdp.version = local.version;
    sdp.origin = local.origin;
    sdp.name = local.name;
    sdp.info = local.info;
    sdp.uri = local.uri;
    sdp.email = local.email;
    sdp.phone = local.phone;
    sdp.connection = local.connection;
    sdp.bandwidth = local.bandwidth;
    sdp.time = local.time;
    sdp.repeatTime = local.repeatTime;
    sdp.timeZone = local.timeZone;
    sdp.key = local.key;
    sdp.attributes = local.attributes;
    
    for (size_t i = 0; i < local.getMediumCount(); ++ i)
    {
        sdp::Medium& medium = local.getMedium(i);
        size_t idx = peer.findMedium(medium.name);
        if (idx == (size_t)-1)
        {
            continue;
        }

        sdp::Medium& peerMedium = peer.getMedium(idx);

        sdp::Medium newMedium(medium.name, medium.port, medium.proto);
        newMedium.attributes = medium.attributes;
        newMedium.connection = medium.connection;
        newMedium.bandwidth = medium.bandwidth;
        newMedium.control = medium.control;

        for (size_t j = 0; j < medium.codecs.size(); ++ j)
        {
            sdp::Codec& codec = medium.codecs[j];
            idx = peerMedium.findCodec(codec.name);
            if (idx == (size_t)-1)
            {
                continue;
            }

            sdp::Codec newCodec(codec);
            newCodec.payload = peerMedium.codecs[idx].payload;
            newMedium.addCodec(newCodec);
        }

        sdp.addMedium(newMedium);
    }

    return sdp.toString();
}

int CMediaPort::setup(const std::string& localSdp, const std::string& peerSdp)
{
    sdp::SessionDescription local;
    local.parse(localSdp);

    sdp::SessionDescription peer;
    peer.parse(peerSdp);

    setupMedium(local, peer, "video", m_vMedium);
    setupMedium(local, peer, "audio", m_aMedium);

    comn::FileUtil::dump(local.toString(), "local.sdp");

    return 0;
}

void CMediaPort::setPath(const std::string& playerPath, const std::string& rtpPath,
                         const std::string& vRtp, const std::string& aRtp)
{
    m_playerPath = playerPath;
    m_rtpPlayPath = rtpPath;
    m_vRtpPath = vRtp;
    m_aRtpPath = aRtp;
}

bool CMediaPort::setupMedium(sdp::SessionDescription& local, sdp::SessionDescription& peer,
                 const char* name, detail::RtpMedium& rtpMedium)
{
    bool found = false;
    size_t localIdx = local.findMedium(name);
    size_t peerIdx = peer.findMedium(name);
    if ((localIdx != -1) && (peerIdx != -1))
    {
        sdp::Medium& localMedium = local.getMedium(localIdx);
        sdp::Medium& peerMedium = peer.getMedium(peerIdx);

        rtpMedium.ip = peer.getConnection(peerMedium).address;
        rtpMedium.port = peerMedium.port;
        rtpMedium.localPort = localMedium.port;
        rtpMedium.mode = localMedium.getMode();

        if (!localMedium.codecs.empty())
        {
            sdp::Codec& localCodec = localMedium.codecs[0];
            rtpMedium.payload = localCodec.payload;

            found = true;
        }

        if (rtpMedium.mode == sdp::kSendRecv)
        {
            localMedium.port += PORT_STEP;
        }
    }
    return found;
}

